/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.engine.entity.convertor;

import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bff.api.manager.IFSManagerContext;
import com.inspur.edp.bff.spi.AbstractChangeConvertor;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.AssoInfoBase;
import com.inspur.edp.cef.entity.i18n.MultiLanguageInfo;
import com.inspur.edp.cef.spi.entity.IAuthFieldValue;
import com.inspur.edp.commonmodel.engine.api.data.AssociationInfo;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import lombok.var;

import java.util.HashMap;
import java.util.Map;

public class BffEngineChangeConvertor extends AbstractChangeConvertor {

    private GspViewObject voObject;
    private IFSManagerContext _mgrContext;
    private GspBizEntityObject beObject;
    private Map<String, IGspCommonObject> childs;

    public BffEngineChangeConvertor(IFSManagerContext mgrContext, GspViewObject voObject, GspBizEntityObject beObject) {
        super(mgrContext);
        _mgrContext = mgrContext;
        this.beObject = beObject;
        this.voObject = voObject;
    }

    @Override
    protected void convertModifyPropFromBE(String beElementCode, Object beValue, ModifyChangeDetail voChange) {

        for (var voElement : voObject.getContainElements()) {
            if ((voElement.getIsVirtual() && ((GspViewModelElement)voElement).getIsVirtualViewElement()) || voElement.getObjectType() == GspElementObjectType.DynamicProp)
                continue;
            var beElement = beObject.findElement(((GspViewModelElement) voElement).getMapping().getTargetElementId());
            if (beElement == null || !beElement.getLabelID().equals(beElementCode))
                continue;

            if (voElement.getIsUdt()) {
                voChange.getPropertyChanges().put(voElement.getLabelID(), beValue);
            } else if (((GspViewModelElement) voElement).isEnableMultiLanguageInput() && beElementCode.equals(
                    beElement.getLabelID() + MultiLanguageInfo.MULTILANGUAGETOKEN)) {
                super.setChangeDetailMultiLanguageInfo(voChange, voElement.getLabelID(), beValue);
            } else {
                var value = getTransDataValueFromBe(beElement, (IGspCommonElement) voElement, beValue);
                voChange.getPropertyChanges().put(voElement.getLabelID(), value);
            }
        }

    }

    private Object getTransDataValueFromBe(IGspCommonElement sourceEle, IGspCommonElement targetEle, Object value) {
        if (sourceEle.getObjectType() == GspElementObjectType.Association) {
            if (value == null)
                return value;
            if (value instanceof AssoInfoBase) {
                com.inspur.edp.commonmodel.engine.api.data.AssociationInfo info = new AssociationInfo();
                AssoInfoBase infoBase = (AssoInfoBase) value;
                info.setValue(targetEle.getLabelID(), ((IAuthFieldValue) infoBase).getValue());
                for (IGspCommonField refElement : targetEle.getChildAssociations().get(0).getRefElementCollection()) {
                    info.setValue(refElement.getLabelID(), infoBase.getValue(refElement.getLabelID()));
                }
                return info;
            }
            if (value instanceof com.inspur.edp.commonmodel.engine.api.data.AssociationInfo) {
                try {
                    return ((AssociationInfo) value).clone();
                } catch (CloneNotSupportedException e) {
                    throw new RuntimeException(e);
                }
            }
            throw new RuntimeException("无法识别的关联类型.");
        } else if (sourceEle.getObjectType() == GspElementObjectType.Enum) {
            if (value == null)
                return value;
            if (value.getClass().isEnum()) {
                return value.toString();
            }
            return (String) value;
        }
        return value;

    }

    private Object getTransDataValue(IGspCommonElement sourceEle, IGspCommonElement targetEle, Object value) {

        if (sourceEle.getObjectType() == GspElementObjectType.Association) {
            if (_mgrContext.getLcp().getClass().getSimpleName().equals("EngineStandardLcp") == false) {
                Object targetValue = _mgrContext.getLcp()
                        .createPropertyDefaultValue(beObject.getCode(), targetEle.getLabelID());

                AssoInfoBase targetAssInfo = (AssoInfoBase) targetValue;
                setAssoValues((AssociationInfo) value, targetAssInfo);
                return targetAssInfo;
            }
            try {
                return value == null ? null
                        : ((com.inspur.edp.commonmodel.engine.api.data.AssociationInfo) value).clone();
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException("字段" + sourceEle.getLabelID() + "的关联值克隆失败", e);
            }
        } else if (sourceEle.getObjectType() == GspElementObjectType.Enum) {
            if (_mgrContext.getLcp().getClass().getSimpleName().equals("EngineStandardLcp") == false) {
                Object targetValue = _mgrContext.getLcp()
                        .createPropertyDefaultValue(beObject.getCode(), targetEle.getLabelID());
                if (targetValue instanceof String)
                    return value;
                if (targetValue instanceof Enum) {
                    Class<Enum> class1 = (Class<Enum>) targetValue.getClass();
                    return Enum.valueOf(class1, (String) value);
                }
            } else return value;
        }
        return value;

    }

    private void setAssoValues(AssociationInfo value, AssoInfoBase targetAssInfo) {

        for (String itemKey : value.getPropertyNames()) {
            Object itemValue = value.getValue(itemKey);
            if (itemValue instanceof AssociationInfo) {
                AssoInfoBase refElementValue = (AssoInfoBase) targetAssInfo.createValue(itemKey);
                targetAssInfo.setValue(itemKey, refElementValue);
                setAssoValues((AssociationInfo) itemValue, refElementValue);
            } else
                targetAssInfo.setValue(itemKey, itemValue);
        }
    }

    @Override
    protected void convertModifyPropFromVM(String vmElementCode, Object vmValue, ModifyChangeDetail beChange) {
        for (var voElement : voObject.getContainElements()) {
            if (!voElement.getLabelID().equals(vmElementCode) || (voElement.getIsVirtual() && ((GspViewModelElement)voElement).getIsVirtualViewElement()))// || voElement.getObjectType() == GspElementObjectType.DynamicProp)
                continue;
            var beElement = beObject.findElement(((GspViewModelElement) voElement).getMapping().getTargetElementId());
            if (beElement == null)
                continue;

            if (voElement.getIsUdt()) {
                beChange.getPropertyChanges().put(beElement.getLabelID(), vmValue);
            } else {
                var value = getTransDataValue((IGspCommonElement) voElement, beElement, vmValue);
                beChange.getPropertyChanges().put(beElement.getLabelID(), value);
            }
        }
    }

    @Override
    protected AbstractChangeConvertor getChildChangeConvertor(String childCode) {
        GspViewObject childObj = (GspViewObject) getChildsMap().get(childCode);
        String beChildObjId = childObj.getMapping().getTargetObjId();
        var beChildObj = beObject.getChildObjectById(beChildObjId);
        if (beChildObj == null)
            throw new CAFRuntimeException("", "",
                    "节点【" + childObj.getCode() + "】对应的be节点没有找到，be节点Id为" + beChildObjId, null, ExceptionLevel.Error);
        return new BffEngineChangeConvertor(getMgrContext(), childObj, (GspBizEntityObject) beChildObj);
    }

    @Override
    protected void convertMultiLanguageModifyPropFromBE(String beElementCode, Object beValue, ModifyChangeDetail vmChange) {
        for (var voElement : voObject.getContainElements()) {
            if (voElement.getIsVirtual() || voElement.getObjectType() == GspElementObjectType.DynamicProp)
                continue;
            var beElement = beObject.findElement(((GspViewModelElement) voElement).getMapping().getTargetElementId());
            if (beElement == null || !beElement.getLabelID().equals(beElementCode))
                continue;

            if (((GspViewModelElement) voElement).isEnableMultiLanguageInput()) {
                super.setChangeDetailMultiLanguageInfo(vmChange, voElement.getLabelID(), beValue);
            }
        }
    }

    @Override
    protected void convertMultiLanguageModifyPropFromVo(String vmElementCode, Object vmValue, ModifyChangeDetail beChange) {
        GspViewModelElement vmElement = (GspViewModelElement) voObject.getElementByLabelId(vmElementCode);
        if (!vmElement.isEnableMultiLanguageInput())
            return;
        var beElement = beObject.findElement(vmElement.getMapping().getTargetElementId());
        super.setChangeDetailMultiLanguageInfo(beChange, beElement.getLabelID(), vmValue);
    }


    private Map<String, IGspCommonObject> getChildsMap() {
        if (childs == null) {
            childs = new HashMap<>(voObject.getContainChildObjects().size());
            for (IGspCommonObject node : voObject.getContainChildObjects()) {
                childs.put(node.getCode(), node);
            }
        }
        return childs;
    }
}
